package jp.sourceforge.akjrcp.ffmpeg;

import com.akjava.lib.standards.process.ProcessMonitor;
import com.akjava.lib.standards.process.SimpleProcessPrinter;

import java.io.File;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;



public class FFMpegFLVParser {
private String ffmpegPath;
public static Log log = LogFactory.getLog(FLVInfomation.class);
public FFMpegFLVParser(String ffmpeg){
	this.ffmpegPath=ffmpeg;
}
private int audioIndex;
private String logText;
private long maxoverwait=3000;

public FLVInfomation parseFile(File file){
	audioIndex=0;
	FLVInfomation info=new FLVInfomation();
	
	Runtime runtime=Runtime.getRuntime();
	String[] command=new String[]{ffmpegPath,"-i",file.getAbsolutePath()};
	
	
		Process process;
		try {
			process = runtime.exec(command);
			SimpleProcessPrinter printer=new SimpleProcessPrinter();
			ProcessMonitor monitor=new ProcessMonitor(process,printer);
			monitor.startMonitor();
			try {
				process.waitFor();
				
				long c=System.currentTimeMillis();
				while(System.currentTimeMillis()<c+maxoverwait){
					if(monitor.isErrorClosed() && monitor.isInputClosed()){
						break;
					}else{
						log.trace("wait:input reading");
						Thread.sleep(100);
					}
				}
			} catch (InterruptedException e) {
				log.warn("wait:input reading");
				e.printStackTrace();
			}
			Pattern ratePattern=Pattern.compile("Seems that stream.*-> (.*) \\(.*");
			Pattern ratePattern2=Pattern.compile("Seems stream .* codec frame rate differs from container frame rate:.*-> (.*) \\(.*");
			
			
			Pattern videoPattern=Pattern.compile(".*Stream #.* Video:(.*)$");
			Pattern audioPattern=Pattern.compile(".*Stream #.* Audio:(.*)$");
			
			logText=printer.getLog();
			info.setText(StringUtils.replace(logText, "\n", "\r\n"));
			String lines[]=printer.getLog().split("\n");
			for (int i = 0; i < lines.length; i++) {
				Matcher match=ratePattern.matcher(lines[i]);
				Matcher match2=ratePattern2.matcher(lines[i]);
				int matchDuration=lines[i].indexOf("Duration:");
				if(match.matches()){
					info.setVideoRate(Double.parseDouble(match.group(1)));
				}else if(match2.matches()){
					info.setVideoRate(Double.parseDouble(match2.group(1)));
				}else if(matchDuration!=-1){//duration line.
					String d_s_b[]=lines[i].split(",");
					String duration=d_s_b[0].substring(matchDuration+"Duration: ".length());
					if(duration.equals("N/A")){//unknown is 0
						duration="00:00:00.0";
					}
					info.setDuration(duration);
				}
				else{
					//log.infonot much:"+lines[i]);
					match=videoPattern.matcher(lines[i]);
					//log.info(match);
					//sample
					 //Stream #0.0: Video: mpeg4, yuv420p, 640x480, 29.97 fps(r)
					//Stream #0.0[0x1e0]: Video: mpeg1video, yuv420p, 320x240, 104857 kb/s, 29.97 fps(r)
					if(match.matches()){
						
						try{
						log.trace(lines[i]);
						info.setHaveVideo(true);
					//	log.info("video mutch");
						String values[]=match.group(1).replaceAll(" ", "").split(",");
						if(values.length>=3){
							parseVideoCodec(info,values[0]);
							//values[1] is  color map
							parseVideoSize(info,values[2]);
							//values[3] is  frame rate,but old ffmpeg has a problem
							
							
							if(values.length>=4){
								if(values[3].endsWith("fps(r)")){	//parse around 9133 ffmpeg
									parseVideoRate(info,values[3]);
								}else if(values[3].endsWith("tb(r)")){	//parse around 9133 ffmpeg
									parseVideoRateForNewVersion(info,values[3]);
								}else{
//								4:4is kbps;
									
//								5:parse rate
								if(values.length>=5){
									if(values[4].endsWith("tb(r)")){
										parseVideoRateForNewVersion(info,values[4]);
									}else{
										parseVideoRate(info,values[4]);
									}
									}
								}
							}
							
							
						}
						}catch(Exception e){
							//anyway do it 
							e.printStackTrace();
							log.warn(e.getMessage());
						}
					}else{
						//much audio patter.
						//ffmpeg rev-14537 //Stream #0.1: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s
						match=audioPattern.matcher(lines[i]);
						if(match.matches()){
							try{
							log.trace(lines[i]);
							info.setHaveAudio(true);
							String values[]=match.group(1).replaceAll(" ", "").split(",");
							info.setAudioCodec(values[0],audioIndex);
							if (values.length > 1) {
								
								
								int audioRate ;
								if(values[1].toLowerCase().endsWith("hz")){
								audioRate= Integer.parseInt(values[1].substring(0, values[1]
										.length() - 2));// last char is 'Hz'
							}else if(values[1].toLowerCase().endsWith("kbhz")){
								audioRate= Integer.parseInt(values[1].substring(0, values[1]
								                  										.length() - 4));
							}else if(values[1].toLowerCase().endsWith("kb/s")){
								audioRate= Integer.parseInt(values[1].substring(0, values[1]
									                  										.length() - 4));
								}else{
								audioRate=0;//unknown.
							}
								info.setAudioRate(audioRate,audioIndex);
								if (values.length > 2) {
									int audioChannel=0;
									if (values[2].equals("stereo")) {
										audioChannel = 2;
									} else if (values[2].equals("mono")) {
										audioChannel = 1;
									}else{
										log.warn("unknown channel:"+values[2]);
									}
									info.setAudioChannel(audioChannel,audioIndex);
									if (values.length > 3) {
										String bitString=null;
										if(values[3].toLowerCase().endsWith("kb/s")){
											bitString=values[3];
										}else if(values.length>4 && values[4].toLowerCase().endsWith("kb/s")){
											bitString=values[4];
										}
										if(bitString!=null){
										int audioBitrate = Integer.parseInt(bitString
												.substring(0,bitString.length() - 4));// kb/s
										info.setAudioBitrate(audioBitrate,audioIndex);
										}
									}
								}
							}
							audioIndex++;
							}catch(Exception e){
								//anyway do it 
								e.printStackTrace();
								log.warn(e.getMessage());
							}
						}
						
					}
				}
			}
		} catch (IOException e1) {
			
			e1.printStackTrace();
		}

		
	return info;
}
private void parseVideoRate(FLVInfomation info,String string) {
	//29.97 fps(r)
	String value=StringUtils.replace(string, "fps(r)","");
	try{
		info.setVideoRate(Double.parseDouble(value));
	}catch(Exception e){
		log.warn(e.getMessage());
	}
}

private void parseVideoRateForNewVersion(FLVInfomation info,String string) {
	//29.97 fps(r)
	String value=StringUtils.replace(string, "tb(r)","");
	try{
		info.setVideoRate(Double.parseDouble(value));
	}catch(Exception e){
		log.warn(e.getMessage());
	}
}
private void parseVideoSize(FLVInfomation info,String string) {
	String wh[]=string.split("x");
	if(wh.length==2){
		info.setVideoWidth(Integer.parseInt(wh[0]));
		if(wh[1].indexOf("[")!=-1){
			//for like Stream #0.0[0x1011]: Video: h264, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 29.97 tb(r)
			String h_info[]=wh[1].split("\\[");
			info.setVideoHeight(Integer.parseInt(h_info[0]));
			
			//support aspect ratio
			if(string.indexOf("DAR16:9")!=-1){//space already removed
				info.setAspectRatio("16:9");
			}else if(string.indexOf("DAR4:3")!=-1){//space already removed
				info.setAspectRatio("4:3");
			}
		}else{
			//old one
			info.setVideoHeight(Integer.parseInt(wh[1]));
		}
	}
}
private void parseVideoCodec(FLVInfomation info,String string) {
	if(string.equals(FLVInfomation.FLV1)){
		info.setVideoCodec(FLVInfomation.FLV1);
	}else if(string.equals(FLVInfomation.FLV4)){
		info.setVideoCodec(FLVInfomation.FLV4);
	}else{
		info.setVideoCodec(string);
	}
}

public String getLogText() {
	return logText;
}

public long getMaxoverwait() {
	return maxoverwait;
}

public void setMaxoverwait(long maxoverwait) {
	this.maxoverwait = maxoverwait;
}

}
